package com.guo.autoscan.ssh2;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.telnet.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class TelnetUtil implements Runnable, TelnetNotificationHandler
{
    Logger logger = LoggerFactory.getLogger(getClass());
    private TelnetClient tc = null;
    private String remoteip;
    private int remoteport;
    private String remoteName;
    private String username;
    InputStream instr;
    private StringBuffer responseStr = new StringBuffer();

    public TelnetUtil(String ip, int port)
    {
        this.remoteip = ip;
        this.remoteport = port;

        initClient();
    }

    public boolean isConnected()
    {
        return this.tc.isConnected();
    }

    public TelnetUtil(String ip, int port, String username, String password, String deviceName)
            throws Exception
    {
        this(ip, port);
        login(username, password);
        this.remoteName = deviceName;
        this.username = username;
    }

    private void initClient()
    {
        this.tc = new TelnetClient();

        TerminalTypeOptionHandler ttopt = new TerminalTypeOptionHandler("VT220", false, false, true, false);

        EchoOptionHandler echoopt = new EchoOptionHandler(true, false, true, false);

        SuppressGAOptionHandler gaopt = new SuppressGAOptionHandler(true, true, true, true);
        try
        {
            this.tc.addOptionHandler(ttopt);
            this.tc.addOptionHandler(echoopt);
            this.tc.addOptionHandler(gaopt);
        }
        catch (Exception e)
        {
            this.logger.error(e.getMessage());
        }
    }

    private String connect(long waitTime)
            throws Exception
    {
        try
        {
            this.tc.connect(this.remoteip, this.remoteport);


            this.tc.registerNotifHandler(this);
            this.instr = this.tc.getInputStream();


            return getResponse(waitTime);
        }
        catch (Exception e)
        {
            throw new Exception("telnet 连接失败", e);
        }
    }

    public void login(String username, String password)
            throws Exception
    {
        String connectStr=connect(2000L);
        logger.info(connectStr);
        logger.info("输入用户名："+username);
        sendCommand(username,500);
        logger.info("输入密码："+password);
        sendCommand(password,500);
    }

    public void disConnect()
            throws IOException
    {
        if ((this.tc != null) && (this.tc.isConnected())) {
            this.tc.disconnect();
        }
    }

    public void sendCommand(String command)
            throws Exception
    {
        try
        {
            this.responseStr.delete(0, this.responseStr.capacity());
            OutputStream outstr = this.tc.getOutputStream();
            if (StringUtils.isBlank(command)) {
                command = "\n";
            }
            outstr.write(command.getBytes());
            outstr.write(13);
            outstr.write(10);
            outstr.flush();
        }
        catch (Exception e)
        {
            throw new Exception("telnet 发送命令[" + command + "]失败", e);
        }
    }

    public String sendCommand(String command, int waitTime)
            throws Exception
    {
        try
        {
            this.responseStr.delete(0, this.responseStr.capacity());
            OutputStream outstr = this.tc.getOutputStream();
            outstr.write(command.getBytes());
            outstr.write(13);
            outstr.write(10);
            outstr.flush();
        }
        catch (Exception e)
        {
            throw new Exception("telnet 发送命令[" + command + "]失败", e);
        }
        return getResponse(command, waitTime);
    }

    public void receivedNegotiation(int negotiation_code, int option_code)
    {
        String command = null;
        if (negotiation_code == 1) {
            command = "DO";
        } else if (negotiation_code == 2) {
            command = "DONT";
        } else if (negotiation_code == 3) {
            command = "WILL";
        } else if (negotiation_code == 4) {
            command = "WONT";
        }
        this.logger.debug("Received " + command + " for option code " + option_code);
    }

    public void run()
    {
        try
        {
            byte[] buff = new byte[1024];
            int ret_read = 0;
            do
            {
                ret_read = this.instr.read(buff);
                if (ret_read > 0) {
                    this.responseStr.append(new String(buff, 0, ret_read).replaceAll("\t", ""));
                }
            } while (ret_read >= 0);
        }
        catch (Exception e)
        {
            this.logger.error("Exception while reading socket:" + e.getMessage());
        }
    }

    private String getResponse(long waitTime)
    {
        return readUntil(null, waitTime);
    }

    public String getResponse(String cmd, long waitTime)
    {
        String pattern1 = this.remoteName;
        String pattern2 = this.username + "@" + this.remoteName;
        StringBuffer buffer = new StringBuffer();
        String msg = getResponse(waitTime);
        msg = msg.replaceAll("\r", "");
        String[] retMsgs = msg.split("\n");
        for (String str : retMsgs) {
            if (pattern1!=null && (!str.startsWith(pattern1)) && (!str.startsWith(pattern2))) {
                buffer.append(str + "\n");
            }
        }
        if(StringUtils.containsIgnoreCase(cmd,"display interface Ten-GigabitEthernet")) {
            try {
                String temStr= this.sendCommand(" ", 500);
                buffer.append(temStr);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return buffer.toString().trim();
    }

    public String readUntil(String cmd, long waitTime)
    {
        StringBuffer sb = new StringBuffer();
        try
        {
            int code = -1;
            int available = this.instr.available();
            if (available == 0)
            {
                Thread.sleep(waitTime);
                available = this.instr.available();
            }
            while (available != 0)
            {

                available = this.instr.available();
                if (available == 0)
                {
                    Thread.sleep(waitTime);
                    available = this.instr.available();
                }
                else
                {
                    code = this.instr.read();
                    char ch = (char)code;
                    sb.append(ch);
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return sb.toString();
    }
}
